Skip to content

Conversation

@jmcarp
Copy link
Contributor

@jmcarp jmcarp commented Jan 28, 2026

The sdk contains a small number of untagged unions: union types that lack a discriminator field. Because we don't have a field that tells us which variant to use, we currently model these types as interface{}.

This patch introduces interface types with variant markers for untagged unions whose fields can be distinguished using OpenAPI format or pattern metadata. On unmarshal, check each format or pattern field against the incoming data, and hydrate into the first matching variant type.

As for previous code generation changes, review DESIGN.md first, then the generated code. The code generation code is still WIP.

@jmcarp jmcarp force-pushed the jmcarp/untagged-unions branch from 107c4e6 to de86fcb Compare January 28, 2026 19:43
Copy link
Collaborator

@sudomateo sudomateo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach looks good to me! I left some comments around a few areas but overall I like the approach.


func detectIPv6Format(s string) bool {
ip := net.ParseIP(s)
return ip != nil && ip.To4() == nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh Go. The difference between this and the function above it is quite subtle. I wish there were IsV4 and IsV6 methods or something.

oxide/types.go Outdated

func (v IpNet) MarshalJSON() ([]byte, error) {
if v.Value == nil {
return []byte("null"), nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to explicitly write null here instead of letting Go use the struct field tags omit*?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting rid of this part. encoding/json can handle nil values without any special logic here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This design makes sense to me! My only question is do we only use this untagged style when the variants are the same type? I know in Rust they aren't the same type but I mean their serialized form. That is, IpNet variants are all string and IpRange are all struct{first, last}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case we're considering here is string fields with either format or pattern in the openapi spec. At the moment, that's how all untagged unions work in nexus, although obviously this could change in the future. In the case where different variants use different types, we could think about attempting to unmarshal the data into each variant, and accepting the first variant type that works.

@jmcarp jmcarp force-pushed the jmcarp/untagged-unions branch 4 times, most recently from ce15434 to ef79707 Compare January 29, 2026 18:48
@jmcarp jmcarp force-pushed the jmcarp/untagged-unions branch 3 times, most recently from 74117ab to 04cdfe7 Compare January 29, 2026 19:45
The sdk contains a small number of untagged unions: union types that lack a
discriminator field. Because we don't have a field that tells us which variant
to use, we currently model these types as `interface{}`.

This patch introduces interface types with variant markers for untagged unions
whose fields can be distinguished using OpenAPI `format` or `pattern` metadata.
On unmarshal, check each `format` or `pattern` field against the incoming data,
and hydrate into the first matching variant type.
@jmcarp jmcarp force-pushed the jmcarp/untagged-unions branch from 04cdfe7 to 4f2d121 Compare January 29, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants